/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import { BaseNode, NodeID } from '../core/graph/BaseExplicitGraph';
import { ArkThrowStmt, Stmt } from '../core/base/Stmt';
import { ArkParameterRef } from '../core/base/Ref';

export type StmtOrFormalParm = Stmt |ArkParameterRef;

export enum VFGNodeKind {
    Assignment, Copy, Write, Load, AddrObj, If, ActualParm, FormalParm, ActualRet,
    FormalRet, Unary, Binary, Normal, Throw,
}

export class DVFGNode extends BaseNode {
    private stmtOrParm: StmtOrFormalParm;

    constructor(i: NodeID, k: VFGNodeKind, s: StmtOrFormalParm) {
        super(i, k);
        this.stmtOrParm = s;
    }

    public getDotAttr(): string {
        switch (this.getKind()) {
            case VFGNodeKind.AddrObj:
                return 'shape=box,color=green';
            case VFGNodeKind.Load:
                return 'shape=box,color=red';
            case VFGNodeKind.Write:
                return 'shape=box,color=blue';
            case VFGNodeKind.If:
                return 'shape=box,color=rosybrown';
            case VFGNodeKind.ActualParm:
            case VFGNodeKind.ActualRet:
            case VFGNodeKind.FormalRet:
            case VFGNodeKind.FormalParm:
                return 'shape=box,color=yellow';
            default:
                return 'shape=box';
        }
    }

    public getDotLabel(): string {
        let label: string;

        label = VFGNodeKind[this.getKind()];
        label = label + ` ID: ${this.getID()}`;
        label = label + `\n${this.stmtOrParm.toString()}`;

        if (this.stmtOrParm instanceof Stmt) {
            let method = this.stmtOrParm.getCfg()?.getDeclaringMethod().getSubSignature().toString();
            if (method) {
                label = label + '\n' + method;
            }
            label = label + ' ln: ' + this.stmtOrParm.getOriginPositionInfo().getLineNo();
        } else {
            // TODO: add method info
        }

        return label;
    }

    public getStmtOrFP(): StmtOrFormalParm {
        return this.stmtOrParm;
    }

    public isStmt(): boolean {
        return this.stmtOrParm instanceof Stmt;
    }

    public isFormalParm(): boolean {
        return this.stmtOrParm instanceof ArkParameterRef;
    }
}

export class AssignVFGNode extends DVFGNode {
    constructor(i: NodeID, k: VFGNodeKind, s: Stmt) {
        super(i, k, s);
    }
}

export class AddrObjVFGNode extends AssignVFGNode {
    constructor(i: NodeID, s: Stmt) {
        super(i, VFGNodeKind.AddrObj, s);
    }
}

export class CopyVFGNode extends AssignVFGNode {
    constructor(i: NodeID, s: Stmt) {
        super(i, VFGNodeKind.Copy, s);
    }
}

export class LoadVFGNode extends AssignVFGNode {
    constructor(i: NodeID, s: Stmt) {
        super(i, VFGNodeKind.Load, s);
    }
}

export class WriteVFGNode extends AssignVFGNode {
    constructor(i: NodeID, s: Stmt) {
        super(i, VFGNodeKind.Write, s);
    }
}

export class CmpVFGNode extends DVFGNode {
    constructor(i: NodeID, s: Stmt) {
        super(i, VFGNodeKind.If, s);
    }
}

export class ParmVFGNode extends DVFGNode {
    parmLoc: number; // location of actual parameter, start from 0
    constructor(i: NodeID, k: VFGNodeKind, s: StmtOrFormalParm, loc: number) {
        super(i, k, s);
        this.parmLoc = loc;
    }

    public getParmLoc(): number {
        return this.parmLoc;
    }
}

export class ActualParmVFGNode extends ParmVFGNode {
    defStmt: Stmt;
    constructor(i: NodeID, ivkStmt: Stmt, defStmt: Stmt, loc: number) {
        super(i, VFGNodeKind.ActualParm, ivkStmt, loc);
        this.defStmt = defStmt;
    }

    public getDefStmt(): Stmt {
        return this.defStmt;
    }
}

export class FormalParmVFGNode extends ParmVFGNode {
    constructor(i: NodeID, parm: ArkParameterRef, loc: number) {
        super(i, VFGNodeKind.FormalParm, parm, loc);
    }
}

export class FormalClosureParmVFGNode extends FormalParmVFGNode {
    closureIdx: number;
    constructor(i: NodeID, parm: ArkParameterRef, idx: number) {
        super(i, parm, 0);
        this.closureIdx = idx;
    }

    getClosureIdx(): number {
        return this.closureIdx;
    }
}

export class ActualClosureParmVFGNode extends ActualParmVFGNode {
    closureIdx: number;
    constructor(i: NodeID, ivkStmt: Stmt, defStmt: Stmt, loc: number, idx: number) {
        super(i, ivkStmt, defStmt, loc);
        this.closureIdx = idx;
    }

    getClosureIdx(): number {
        return this.closureIdx;
    }
}

export class ReturnVFGNode extends DVFGNode {
    constructor(i: NodeID, k: VFGNodeKind, s: Stmt) {
        super(i, k, s);
    }
}

export class FormalReturnVFGNode extends ReturnVFGNode{
    constructor(i: NodeID, s: Stmt) {
        super(i, VFGNodeKind.FormalRet, s);
    }
}

export class ActualReturnVFGNode extends ReturnVFGNode{
    constructor(i: NodeID, s: Stmt) {
        super(i, VFGNodeKind.ActualRet, s);
    }
}

export class ThrowVFGNode extends DVFGNode {
    constructor(i: NodeID, s: ArkThrowStmt) {
        super(i, VFGNodeKind.Throw, s);
    }
}